package com.flame.shapeville.bean;

import lombok.Data;
import lombok.NoArgsConstructor;

import java.util.HashMap;
import java.util.Map;

/**
 * Represents an exercise task assigned to a user.
 * <p>
 * Contains metadata, answer status, scoring logic, and runtime parameters.
 *
 * @author Flame Dev Team
 * @version 1.0
 * @since 2025-05-01
 */
@Data
@NoArgsConstructor
public class Exercise {
    /**
     * Unique ID of the exercise.
     */
    private String id;

    /**
     * Reference ID of the corresponding task.
     */
    private String taskId;

    /**
     * Indicates whether this is an advanced-level exercise.
     */
    private Boolean advanced;

    /**
     * Serial number of the exercise within its task.
     */
    private Integer no = 0;

    /**
     * Runtime parameters for the exercise.
     */
    private Map<String, Object> params = new HashMap<>();

    /**
     * Correct answer of the exercise.
     */
    private String answer;

    /**
     * Allowed time (in seconds), -1 means unlimited.
     */
    private Integer time = -1;

    /**
     * Number of incorrect attempts.
     */
    private Integer misCount = 0;

    /**
     * Remaining chances to attempt.
     */
    private Integer chance = 3;

    /**
     * Whether the exercise has been completed.
     */
    private Boolean completed = false;

    /**
     * Whether the exercise timed out.
     */
    private Boolean timeout = false;

    /**
     * Score achieved on this exercise.
     */
    private Integer score = 0;

    /**
     * Constructs a new exercise with the given parameters.
     *
     * @param id       the unique ID of the exercise
     * @param taskId   the ID of the associated task
     * @param advanced indicates if it's an advanced exercise
     * @param no       the serial number of the exercise
     * @param answer   the correct answer
     */
    public Exercise(String id, String taskId, Boolean advanced, Integer no, String answer) {
        this.id = id;
        this.taskId = taskId;
        this.advanced = advanced;
        this.no = no;
        this.answer = answer;
    }

    /**
     * Constructs a new exercise with the given ID, task ID, advanced flag, and serial number.
     *
     * @param id       the unique ID of the exercise
     * @param taskId   the ID of the associated task
     * @param advanced indicates if it's an advanced exercise
     * @param no       the serial number of the exercise
     */
    public Exercise(String id, String taskId, Boolean advanced, Integer no) {
        this.id = id;
        this.taskId = taskId;
        this.advanced = advanced;
        this.no = no;
    }

    /**
     * Constructs a new exercise with the given ID, task ID, advanced flag, serial number, and time limit.
     *
     * @param id       the unique ID of the exercise
     * @param taskId   the ID of the associated task
     * @param advanced indicates if it's an advanced exercise
     * @param no       the serial number of the exercise
     * @param time     the allowed time in seconds (-1 for unlimited)
     */
    public Exercise(String id, String taskId, Boolean advanced, Integer no, Integer time) {
        this.id = id;
        this.taskId = taskId;
        this.advanced = advanced;
        this.no = no;
        this.time = time;
    }

    /**
     * Constructs a new exercise with the given ID, task ID, advanced flag, serial number, time limit, and answer.
     *
     * @param id       the unique ID of the exercise
     * @param taskId   the ID of the associated task
     * @param advanced indicates if it's an advanced exercise
     * @param no       the serial number of the exercise
     * @param time     the allowed time in seconds (-1 for unlimited)
     * @param answer   the correct answer
     */
    public Exercise(String id, String taskId, Boolean advanced, Integer no, Integer time, String answer) {
        this.id = id;
        this.taskId = taskId;
        this.advanced = advanced;
        this.no = no;
        this.time = time;
        this.answer = answer;
    }

    /**
     * Submits an answer and records the response time.
     * Updates completion status and score based on correctness.
     *
     * @param answer the submitted answer
     * @param time   the time taken to respond (in seconds)
     * @return true if the answer was accepted or chances were exhausted; false otherwise
     */
    public boolean answer(String answer, int time) {
        setTime(time);
        if (completed) return false;
        if (this.answer.equals(answer)) {
            completed = true;
            setScore();
            return true;
        }
        misCount++;
        chance--;
        if (chance == 0) {
            completed = true;
        }
        return false;
    }

    /**
     * Sets the score based on correctness and number of mistakes.
     */
    private void setScore() {
        if (advanced) {
            if (misCount == 0) score = 6;
            if (misCount == 1) score = 4;
            if (misCount == 2) score = 2;
        } else {
            if (misCount == 0) score = 3;
            if (misCount == 1) score = 2;
            if (misCount == 2) score = 1;
        }
    }

    /**
     * Adds a parameter to the exercise.
     *
     * @param key   the key of the parameter
     * @param value the value of the parameter
     */
    public void addParam(String key, Object value) {
        params.put(key, value);
    }

    /**
     * Retrieves a parameter by key.
     *
     * @param key the key of the parameter
     * @return the value associated with the key, or null if not found
     */
    public Object getParam(String key) {
        return params.get(key);
    }

    /**
     * Marks the exercise as timed out.
     */
    public void timeout() {
        completed = true;
        timeout = true;
        time = 0;
    }
}
